package com.own.business.resource.module.aliyun;

import com.aliyun.oss.HttpMethod;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.GeneratePresignedUrlRequest;
import com.aliyun.oss.model.MatchMode;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.PolicyConditions;
import com.aliyun.oss.model.StorageClass;
import com.own.business.resource.core.base.AbstractObjectStoreUtil;
import com.own.business.resource.core.model.SignatureModel;
import com.own.business.resource.core.model.base.BaseUploadModel;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.List;

/**
 * AliYunOssUtil
 *
 * @author chenxueli
 * @date 2023-07-12 09:12:13
 */
@Slf4j
public class AliYunOssUtil extends AbstractObjectStoreUtil {

    /**
     * 上传单个文件
     *
     * @param file 文件信息
     * @return 文件存储对象信息
     */
    @Override
    public BaseUploadModel uploadFile(BaseUploadModel file) {
        var oss = getOssClient();
        try {
            upload(oss, file);
        } finally {
            oss.shutdown();
        }
        return file;
    }

    /**
     * 上传单个文件
     *
     * @param fileList 文件信息列表
     * @return 文件存储对象信息列表
     */
    @Override
    public List<BaseUploadModel> uploadFile(List<BaseUploadModel> fileList) {
        var oss = getOssClient();
        try {
            fileList.parallelStream().forEach(file -> upload(oss, file));
        } finally {
            oss.shutdown();
        }
        return fileList;
    }

    /**
     * 验证配置是否正常
     *
     * @return 是否正常
     */
    @Override
    public boolean check() {
        var properties = super.getProperties();
        var name = super.getName();
        var flag = true;
        if (StringUtils.isEmpty(properties.getKey())) {
            log.error("资源管理{}:阿里云oss key填写为空", name);
            flag = false;
        }
        if (StringUtils.isEmpty(properties.getSecret())) {
            log.error("资源管理{}:阿里云oss secret填写为空", name);
            flag = false;
        }
        if (StringUtils.isEmpty(properties.getBucket())) {
            log.error("资源管理{}:阿里云oss bucket填写为空", name);
            flag = false;
        }
        if (StringUtils.isEmpty(properties.getEndPoint())) {
            log.error("资源管理{}:阿里云oss end-point填写为空", name);
            flag = false;
        }
        if (StringUtils.isEmpty(properties.getPosition())) {
            log.error("资源管理{}:阿里云oss position填写为空", name);
            flag = false;
        }
        return flag;
    }

    /**
     * 获取上传文件签名
     *
     * @param uri 资源路径
     * @return 签名对象
     */
    @Override
    public SignatureModel signature(String uri) {
        var properties = super.getProperties();
        // 设置host
        var host = "https://" + properties.getBucket() + "." + properties.getEndPoint();
        var client = getOssClient();
        // 签名有效时间（30秒，即30秒内没有上传文件，表示上传异常）
        var expireTime = 30;
        SignatureModel signature = null;

        try {
            // 签名到期时间戳
            var expireEndTime = System.currentTimeMillis() + expireTime * 1000;
            var expiration = new Date(expireEndTime);
            var conditions = new PolicyConditions();
            conditions.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
            conditions.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, uri);

            var postPolicy = client.generatePostPolicy(expiration, conditions);
            var binaryData = postPolicy.getBytes(StandardCharsets.UTF_8);
            var encodedPolicy = BinaryUtil.toBase64String(binaryData);
            var postSignature = client.calculatePostSignature(postPolicy);

            signature = SignatureModel.builder()
                    .accessId(properties.getKey())
                    .policy(encodedPolicy)
                    .signature(postSignature)
                    .dir(uri)
                    .host(host)
                    .expire(expireEndTime / 1000)
                    .type("aliyun")
                    .build();
        } catch (Exception e) {
            log.error("获取oss签名异常.{}", e.getMessage(), e);
        }
        client.shutdown();
        return signature;
    }

    /**
     * 获取临时访问路径
     *
     * @param path       路径
     * @param expiration 有效时间（秒）
     * @param style      样式
     * @return 临时访问路径
     */
    @Override
    public String temporaryAccessPath(String path, long expiration, String style) {
        // 获取是否是私有读的属性（私有读才需要进行获取临时的地址）
        if (getProperties().getPrivateRead()) {
            var properties = super.getProperties();
            var ossClient = getOssClient();
            // 设置签名URL过期时间，单位为毫秒。本示例以设置过期时间为1小时为例。
            var request = new GeneratePresignedUrlRequest(properties.getBucket(), path, HttpMethod.GET);
            request.setExpiration(new Date(System.currentTimeMillis() + expiration * 1000L));
            if (StringUtils.isNotBlank(style)) {
                request.setProcess(style);
            }
            var url = ossClient.generatePresignedUrl(request);
            // 设置为https路径
            return url.toString().replace("http://", "https://");
        }
        return path;
    }

    /**
     * 获取阿里云OSS客户端对象
     *
     * @return OSS
     */
    private OSS getOssClient() {
        var properties = super.getProperties();
        return new OSSClientBuilder().build(properties.getEndPoint(), properties.getKey(), properties.getSecret());
    }

    /**
     * 上传文件到oss
     *
     * @param oss  oss对象
     * @param file 上传文件的对象
     */
    public void upload(OSS oss, BaseUploadModel file) {
        var properties = super.getProperties();
        try {
            // 创建上传Object的Metadata
            var metadata = new ObjectMetadata();
            // 上传的文件的长度
            metadata.setContentLength(file.getInputStream().available());
            // 指定该Object被下载时的网页的缓存行为
            metadata.setCacheControl("no-cache");
            // 指定该Object下设置Header
            metadata.setHeader("Pragma", "no-cache");
            // 指定该Object被下载时的内容编码格式
            metadata.setContentEncoding("utf-8");
            // 文件的MIME，定义文件的类型及网页编码，决定浏览器将以什么形式、什么编码读取文件。如果用户没有指定则根据Key或文件名的扩展名生成，
            // 如果没有扩展名则填默认值application/octet-stream
            metadata.setContentType(file.getContentType());
            // 指定该Object被下载时的名称（指示MINME用户代理如何显示附加的文件，打开或下载，及文件名称）
            metadata.setContentDisposition("filename/filesize=" + file.getFileName() + "/" + file.getFileSize() + "Byte.");
            // 上传文件 (上传文件流的形式)
            var result = oss.putObject(properties.getBucket(), file.getFolder("/") + "/" + file.getFileName(), file.getInputStream(), metadata);
            log.info("文件上传oss成功:{}->{}", file.getFileName(), result.getETag());
        } catch (Exception e) {
            log.error("上传文件至阿里云OSS服务异常", e);
            log.error("oss_file:{}", file);
        }
    }

    /**
     * 文件解冻
     *
     * @param path 文件路径
     * @return 是否成功
     */
    @Override
    public boolean restore(String path) {
        var properties = super.getProperties();
        try {
            var oss = getOssClient();
            var metadata = oss.getObjectMetadata(properties.getBucket(), path);
            var isCompleted = metadata.isRestoreCompleted();
            if (isCompleted) {
                return true;
            }
            // 校验Object是否为归档类型Object。
            var storageClass = metadata.getObjectStorageClass();
            if (storageClass == StorageClass.Archive) {
                oss.restoreObject(properties.getBucket(), path);
                return oss.getObjectMetadata(properties.getBucket(), path).isRestoreCompleted();
            }
        } catch (Exception e) {
            log.error("文件解冻异常", e);
        }
        return false;
    }

}
